'''Gam  e main module.

Contains the entry point used by the run_game.py script.

Feel free to put all your game code here, or in other modules in this "lib"
directory.
'''
import data

from globals import *
from LogWriter import log

# import Panda stuff
try:
    import direct.directbase.DirectStart
    from pandac.PandaModules import *

    from direct.task import Task
    from direct.actor import Actor
    from direct.interval.MetaInterval import *
    from direct.interval.IntervalGlobal import *
    from direct.gui.DirectGui import *
    from direct.gui.OnscreenText import OnscreenText
    from direct.filter.CommonFilters import CommonFilters

    #for the events
    from direct.showbase import DirectObject
except:
    log.write('raise', "ROBO-ERROR: NO PANDA LIBS FOUND. PUNY HUMAN SHOULD MAKE SURE THEY ARE RUNNING PPYTHON (NOT PYTHON, STUPID HUMAN!) WITH PANDA-1.5.0. PUNY HUMAN DID NOT READ README FILE... IS PUNY HUMAN TOO STUPID TO COMPUTE TEXT? HA. HA. HA.") 
##try:
##    import ode
##except:
##    log.write('raise', "ROBO-ERROR: NO ODE LIB FOUND. PUNY HUMAN SHOULD MAKE SURE THAT PYODE IS INSTALLED IN <PANDAPATH>/PYTHON/LIBS/SITE-PACKAGES. PUNY HUMAN DID NOT READ README FILE... IS PUNY HUMAN TOO STUPID TO COMPUTE TEXT? HA. HA. HA.")


import procmodels
from Camera import RoboCamera
from Weapon import Weapon
from Explosion import *
from AudioMgr import audio
##from SkyBox import SkyBox

loadPrcFile('../Config.prc')

import math
import random
import sys
import time

random.seed(time.time())


class Robot(object):
    '''
    DOMO ARIGATO MR. ROBOTO!
    '''
    def __init__(_, worldmgr):
        _.dead = False
        _.prevtime = 0.0
        _.worldmgr = worldmgr

        # main body
        _.robo = procmodels.loadModel(procmodels.robo_torso)
        _.robo.setName('robot')
        _.robo.reparentTo(render)

        # head!
        _.head = procmodels.loadModel(procmodels.robo_head) 
        _.head.reparentTo(_.robo)
        _.head.setPos(0,0,1.4)

        _.lazoreyes = [
                Weapon(_.worldmgr, _.robo, procmodels.lazor,
                       0.5,  # cooldown
                       40.0, # drain
                       20.0, # speed
                       Point3(-0.5,0.5,1.8) # offset
                ),
                Weapon(_.worldmgr, _.robo, procmodels.lazor, 0.4, 40.0, 20.0,Point3(0.5,0.5,1.8)),
        ]

        # shoulders + arms
        _.lshoulder = procmodels.loadModel(procmodels.joint)
        _.rshoulder = procmodels.loadModel(procmodels.joint)
        _.lshoulder.reparentTo(_.robo)
        _.rshoulder.reparentTo(_.robo)
        _.lshoulder.setPos(-1.2, 0, 0.4)
        _.rshoulder.setPos( 1.2, 0, 0.4)

        _.lshoulder.setR(15.0)
        _.rshoulder.setR(-15.0)
        
        _.rarm = procmodels.loadModel(procmodels.robo_arm)
        _.larm = procmodels.loadModel(procmodels.robo_arm)
        _.rarm.reparentTo(_.rshoulder)
        _.larm.reparentTo(_.lshoulder)
       
        # hips + legs
        _.rhip = procmodels.loadModel(procmodels.joint)
        _.lhip = procmodels.loadModel(procmodels.joint)
        _.lhip.reparentTo(_.robo)
        _.rhip.reparentTo(_.robo)
        _.lhip.setPos(-0.5, 0, -1.0)
        _.rhip.setPos( 0.5, 0, -1.0)

        _.rleg = procmodels.loadModel(procmodels.robo_leg)
        _.lleg = procmodels.loadModel(procmodels.robo_leg)
        _.rleg.reparentTo(_.rhip)
        _.lleg.reparentTo(_.lhip)

        _.controls = {
            'forward' : False,
            'back' : False,
            'turnRight' : False,
            'turnLeft' : False,
            'strafeLeft' : False,
            'strafeRight' : False,
            'grabby' : False,
            'pewpew_high' : False,
            'pewpew_mid' : False,
            'pewpew_low' : False,
        }

        # whether the robot is holding something or not
        _.hasCheesburgar = False

        _.roboLIFE = 100.0
        _.roboNRG = 100.0
        _.roboRECHARGE = ROBO_RECHARGE
        _.roboPOINTS = 0

        _.walkspeed = 5.0
        _.turnspeed = 40.0
    
        # status readout stuff
        _.points_text = OnscreenText(text='ROBO-POINTS: ', scale=0.06,
            pos=(0,0.9,0.6), fg=(1.0,1.0,1.0,1.0), align=TextNode.ALeft,
            shadow=(0,0,0,1), mayChange=True)#, borderWidth=1

        _.roboLIFE_bar = DirectWaitBar(text = "ROBO-LIFE", value=_.roboLIFE,
            scale=0.5, pos=(-0.8,0, 0.9))
    
        _.roboNRG_bar = DirectWaitBar(text = "ROBO-ENERGY", value=_.roboNRG,
            scale=0.5, pos=(-0.8,0, 0.8), barColor=(0.2,1,0.2,1) )


        # set up fake animations
        hprdest = Vec3(180,0,0)

        _.lshoulder_animation = Sequence(_.lshoulder.hprInterval(0.5, hprdest))
        _.rshoulder_animation = Sequence(_.rshoulder.hprInterval(0.5, hprdest))
        
        forward_dest = Vec3(0,45,0)
        back_dest = Vec3(0,-45,0)

        _.lhip_walk_animation = Sequence(_.lhip.hprInterval(1.0, back_dest), _.rhip.hprInterval(1.0, forward_dest) )
        _.rhip_walk_animation = Sequence(_.rhip.hprInterval(1.0, forward_dest), _.rhip.hprInterval(1.0, back_dest) )

        _.lhip_stand_animation = Sequence(_.lhip.hprInterval(0.5, Vec3(0,0,0)))
        _.rhip_stand_animation = Sequence(_.rhip.hprInterval(0.5, Vec3(0,0,0)))
        
        _.updateTask = taskMgr.add(_.update, 'ROBO-UPDATE')

    def raiseTheRoof(_):
        _.lshoulder_animation.finish()
        _.rshoulder_animation.finish()

        _.lshoulder_animation.start()
        _.rshoulder_animation.start()

    def walk(_):
        _.rhip_walk_animation.finish()
        _.lhip_walk_animation.finish()
        
        _.rhip_walk_animation.start()
        _.lhip_walk_animation.start()

    def update(_, task):
        if _.dead is True:
            return False
        t = task.time - _.prevtime
 
        if _.controls['forward']:
            _.robo.setFluidPos(_.robo, 0,  _.walkspeed * t, 0)
            #_.walk()
        elif _.controls['back']:
            _.robo.setFluidPos(_.robo, 0, -_.walkspeed * t, 0)
            #_.walk()
        else:
            # run standing animation
            _.rhip_walk_animation.finish()
            _.lhip_walk_animation.finish()

            #_.rhip_stand_animation.start()
            #_.lhip_stand_animation.start()
            _.rhip.setHpr(0,0,0)
            _.lhip.setHpr(0,0,0)

        if (_.controls['pewpew_high'] or
            _.controls['pewpew_mid'] or
            _.controls['pewpew_low']):
            
            beam = None
                     
            for eye in _.lazoreyes:
                if _.roboNRG > eye.drain:
                    beam = eye.pewpew()
                    if beam:
                        if _.controls['pewpew_high']:
                            beam.setP(-20)
                        elif _.controls['pewpew_mid']:
                            beam.setP(-30)
                        elif _.controls['pewpew_low']:
                            beam.setP(-45)
            
            # only drain once for both eyes so they always fire at the same time
            if beam:
                _.roboNRG -= eye.drain
                audio.play('pewpew')
            
        if _.controls['grabby']:
            if _.hasCheesburgar:
                # has throwable object in hands, enable power meter
                pass
            else:
                # does not have object ; try to grabby grabby for one
                pass

        if _.controls['strafeRight']:
            _.robo.setFluidPos(_.robo,  (_.walkspeed*0.6) * t, 0, 0)
        elif _.controls['strafeLeft']:
            _.robo.setFluidPos(_.robo, -(_.walkspeed*0.6) * t, 0, 0)
        if _.controls['turnRight']: 
            _.robo.setH(_.robo.getH() - _.turnspeed * t)   
        elif _.controls['turnLeft']:
            _.robo.setH(_.robo.getH() + _.turnspeed * t)

        #update readout
        _.roboLIFE_bar['value'] = _.roboLIFE
        _.roboNRG_bar['value'] = _.roboNRG
        _.points_text['text'] = 'ROBO-POINTS: ' + `_.roboPOINTS`
        
        # remove if I manage to make enemies
        _.roboLIFE -= ROBO_BLEED * t

        if _.roboNRG < 100.0:
            _.roboNRG += _.roboRECHARGE * t

        if _.roboLIFE <= 0.0:
            _.die()
        if _.dead is True:
            return False

        _.prevtime = task.time
        return Task.cont

    def die(_):
        '''
        OMG COMBOMB!
        (play Total Annihilation and you will understand the reference)
        '''
        _.dead = True
        playExplosion(_.robo.getPos(), 5.0, damage=_.worldmgr)
        OnscreenText(text="ROBO-GAME OVER", scale=0.2, fg=(1,0.9,0,1), shadow=(0.6,0,0,1))
        
        _.camREF.setTarget(None)
        _.robo.removeNode()
        del _

class Throwable(object):
    def __init__(_):
        pass

class Enemy(object):
    def __init__(_, style='human'):
        if style == 'human':
            _.node = procmodels.loadModel(procmodels.soldier)
        if style == 'vehicle':
            _.node = procmodels.loadModel(procmodels.tank)
            worldmgr.addPandaBody(_.node, 'tank',
                CollisionSphere()
            )
            
        _.node.reparentTo(render)

class Building(Throwable):
    def __init__(_, worldmgr):
        _.node = procmodels.loadModel(procmodels.building)
        #worldmgr.addBody(_.node, _.node.getScale())#, 2500)
        scale = _.node.getScale()
        worldmgr.addPandaBody(_.node, 'building',
            CollisionTube(0,0,0,
                          0,0,scale.getZ(),
                          0.5),
            collideFrom=False
        )
        _.node.reparentTo(render)

class Controls(DirectObject.DirectObject):
    def __init__(_, puppet, camera):
        _.camera = camera
        _.setPuppet(puppet)
                
        #   W  
        # A S D
        _.accept('w', _.setKey, ["forward", True])
        _.accept('w-up', _.setKey, ["forward", False])
        _.accept('a', _.setKey, ["turnLeft", True])
        _.accept('a-up', _.setKey, ["turnLeft", False])
        _.accept('d', _.setKey, ["turnRight", True])
        _.accept('d-up', _.setKey, ["turnRight", False])
        _.accept('s', _.setKey, ["back", True])
        _.accept('s-up', _.setKey, ["back", False])
        
        # mouse controls and other stuff
        _.accept('r', _.setKey, ["pewpew_high", True])
        _.accept('r-up', _.setKey, ["pewpew_high", False])
        _.accept('f', _.setKey, ["pewpew_mid", True])
        _.accept('f-up', _.setKey, ["pewpew_mid", False])
        _.accept('v', _.setKey, ["pewpew_low", True])
        _.accept('v-up', _.setKey, ["pewpew_low", False])
        _.accept('4', _.setKey, ["grabby", True])
        _.accept('4-up', _.setKey, ["grabby", False])

        _.mousesensitivity = 1.0
        #_.mouseTask = taskMgr.add(_.mouseTaskFunc, 'mouseTask')
        
        # global controls
        _.accept('escape', sys.exit)

    def setKey(_, key, val):
        _.puppet.controls[key] = val

    def setPuppet(_, new):
        _.puppet = new
        try:
            _.camera.setTarget(_.puppet.robo)
        except AttributeError:
            pass



class WorldMgr(DirectObject.DirectObject):
    state = 0
    counter = 0
    lasttime = time.time()
    prevtime = 0.0
    
    humans = []
    cars = []
    soldiers = []
    tanks = []
    
    def __init__(_, size):
        _.fieldsize = size
        _.fieldhalf = _.fieldsize / 2
        
        if ODE_PHYSICS:        
            _.world = ode.World()
            _.world.setGravity((0, 0, -9.81))
            _.world.setERP(0.1)
            _.world.setCFM(1E-4)
            
            _.space = ode.HashSpace()
            #_.space = ode.QuadTreeSpace((0,0,0), (3,3,3), 3)
            _.contactgroup = ode.JointGroup()
    
            _.floor = ode.GeomPlane(_.space, (0, 0, 1), 0)

            _.bodies = []
            _.geoms = []
            _.solids = []
            
            taskMgr.add(_.doPhysics, 'physics')

        else:
            # panda collision stuff only
            base.cTrav = CollisionTraverser()
            if SHOW_COLLISIONS is True:
                base.cTrav.showCollisions(render)
    
            _.cHandler = CollisionHandlerEvent()
            _.cHandler.addInPattern('%fn-into-%in')
    
            # da flo'
            _.floor = procmodels.loadModel(procmodels.gameplane)
            _.floor.setScale(_.fieldsize, _.fieldsize, 1)
            _.floor.reparentTo(render)
    
            # doing it manually here because we NEVER want to see its
            # collision solid
            cNP = _.floor.attachNewNode(CollisionNode('floor'))
            cNP.node().addSolid(
                CollisionPlane(Plane(Vec3(0, 0, 1), Point3(0, 0, 0)))
            )
            cNP.node().setFromCollideMask(BitMask32.allOff())
            base.cTrav.addCollider(cNP, _.cHandler)
    
            _.accept('_bullet-into-building', _.handlePandaDamage)
            _.accept('_bullet-into-human', _.handlePandaDamage)
            _.accept('_bullet-into-car', _.handlePandaDamage)
            _.accept('_bullet-into-floor', _.handlePandaFloor)
            
            _.accept('boom-into-building', _.handlePandaDamage)
            _.accept('boom-into-human', _.handlePandaDamage)
            
            # stomping collisions
            _.accept('robot-into-human', _.handlePandaDamage)
            _.accept('robot-into-car', _.handlePandaDamage)
            _.accept('human-into-robot', _.handlePandaDamage)
            _.accept('human-into-robot', _.handlePandaDamage)
            
            _.accept('robot-into-building', _.handlePandaStop)

        taskMgr.add(_.humanTask, 'humanTask')
        #taskMgr.add(_.doPandaPhysics, 'Pandaphysics')
        
    def addHuman(_):
        point = Point3(random.uniform(0, _.fieldsize-5) - _.fieldhalf - 5,
                       random.uniform(0, _.fieldsize-5) - _.fieldhalf - 5,
                       0
        )
        
        node = procmodels.loadModel(procmodels.human)
        node.setPos(point)
        node.setH(random.randint(0,360))
        
        body = _.addPandaBody(node, 'human',
            CollisionSphere(0,0,0.2,0.15))
        
        node.reparentTo(render)
        _.humans.append(node)
        
    def addCar(_):
        point = Point3(random.uniform(0, _.fieldsize-5) - _.fieldhalf - 5,
                       random.uniform(0, _.fieldsize-5) - _.fieldhalf - 5,
                       0
        )
        
        node = procmodels.loadModel(procmodels.car)
        node.setPos(point)
        node.setH(random.randint(0,3) * 90)
        
        # note : cars currently act just like humans for sake of collisions
        body = _.addPandaBody(node, 'human',
            CollisionSphere(0,0,0.05,0.3))
        
        node.reparentTo(render)
        _.cars.append(node)

    def humanTask(_, task):
        t = task.time - _.prevtime
        
        for h in _.humans:
            h.setFluidPos(h, 0,2.0 * t,0)
            # chance that this human will change direction this frame
            if random.randint(0,100) <= 1:
                h.setH(random.randint(0, 359))
            elif h.getPos().getX() <= -_.fieldhalf:
                h.setH(random.randint(10, 170))
            elif h.getPos().getX() >= _.fieldhalf:
                h.setH(random.randint(190, 359))
            elif h.getPos().getY() <= -_.fieldhalf:
                h.setH(random.randint(10, 80))
            elif h.getPos().getY() >= _.fieldhalf:
                h.setH(random.randint(100, 260))

        
        for c in _.cars:
            c.setFluidPos(c, 0,3.0 * t,0)
            if random.randint(0,100) <= 1:
                c.setH(random.randint(0,3) * 90 )
            elif h.getPos().getX() <= -_.fieldhalf:
                h.setH(90)
            elif h.getPos().getX() >= _.fieldhalf:
                h.setH(270)
            elif h.getPos().getY() <= -_.fieldhalf:
                h.setH(180)
            elif h.getPos().getY() >= _.fieldhalf:
                h.setH(0)
        
        if len(_.humans) < HUMANS:
            _.addHuman()
        
        if len(_.cars) < CARS:
            _.addCar()
            
        _.prevtime = task.time

        return Task.cont 

    def addPandaBody(_, node, name, csolid=None, collideInto=True, collideFrom=True):        
        cNP = node.attachNewNode(CollisionNode(name))
        if not collideInto:
            cNP.node().setIntoCollideMask(BitMask32.allOff())
        if not collideFrom:
            cNP.node().setFromCollideMask(BitMask32.allOff())
        
        if csolid is not None:    
            cNP.node().addSolid(csolid)
        base.cTrav.addCollider(cNP, _.cHandler)
        if DEBUG is True:
            cNP.show()

    def handlePandaDamage(_, entry):
        if DEBUG is True:
            log.write('debug', entry)
            
        fromNP = entry.getFromNodePath()
        intoNP = entry.getIntoNodePath()        
        
        if 'robot' == intoNP.getName() and fromNP.getName() != '_bullet':
            # things running into the robot DIE
            # just swap the roles        
            temp = fromNP
            fromNP = intoNP
            intoNP = temp

        # meh... can't run correct delete function from 'here'
        elif '_bullet' == fromNP.getName():
            fromNP.getParent().removeNode()

        #if 'robot' == fromNP.getName():
        if SCORE.has_key(intoNP.getName()):
            #score!
            _.roboREF.roboPOINTS += SCORE[intoNP.getName()]

        # convert NP into its parent, if possible
        try:
            intoNP = intoNP.getParent()
        except:
            pass

        # no real damage right now, just immediate death. :D
        boomPos = intoNP.getPos()
        boomSize = intoNP.getScale().getZ() * 2
        intoNP.removeNode()
        playExplosion(boomPos, boomSize)
        audio.playRandomSound('boomSounds')

        
    def handlePandaFloor(_, entry):
        fromNP = entry.getFromNodePath()
        # meh... can't run correct delete function from 'here'
        if '_bullet' == fromNP.getName():
            fromNP.getParent().removeNode()
            playExplosion(entry.getSurfacePoint(render), 1.5, damage=_)
        
    def handlePandaStop(_, entry):
        if DEBUG is True:
            log.write('debug', entry)
            
        fromNP = entry.getFromNodePath().getParent()
        
        # push in the direction opposite of the collision angle
        vec = entry.getSurfaceNormal(fromNP)
        vec.setZ(0)
        fromNP.setPos(fromNP, vec * 1.0)


    def addBody(_, node, bodyScale, mass=None):
        '''
        Provide a "mass" argument to make this object physics-aware. Otherwise,
        it will participate in collision detection but not physics.
        '''
        _.addPandaBody(node, bodyScale, mass)
        return

        geom = ode.GeomBox(_.space, lengths=(bodyScale.getX(), bodyScale.getY(), bodyScale.getZ()))
        
        if mass is not None:
            body = ode.Body(_.world)
            M = ode.Mass()
            M.setBox(mass, bodyScale.getX(), bodyScale.getY(), bodyScale.getZ())
           
            body.setMass(M)
            geom.setBody(body)

            _.bodies.append(body)
        
        # create a visible solid for this
        if DEBUG is True:
            debugbb = loader.loadModel(DATAPATH+'cube')
            debugbb.setScale(bodyScale.getX(), bodyScale.getY(), bodyScale.getZ())
            if mass is not None:
                debugbb.setColor(1.0, 0.5, 0.0, 0.5)
            else:
                debugbb.setColor(1.0, 1.0, 1.0, 0.5)

            debugbb.setTransparency(TransparencyAttrib.MAlpha)
            debugbb.reparentTo(node)
       
        _.geoms.append(geom)
        _.solids.append(node)

        
    def near_callback(_, args, geom1, geom2):
        # Check if the objects do collide
        contacts = ode.collide(geom1, geom2)
   
        # Create contact joints
        _.world, _.contactgroup = args
        for c in contacts:
            c.setBounce(0.05)
            c.setMu(5000)
            joint = ode.ContactJoint(_.world, _.contactgroup, c)
            joint.attach(geom1.getBody(), geom2.getBody())

    def doPandaPhysics(_, task):
        t = task.time - _.prevtime
        
        

        _.prevtime = task.time

        return Task.cont 

    def doPhysics(_, task):
        dt = 1.0 / 50.0
        #t = dt - (task.time - _.prevtime)
        
        t = task.time - _.prevtime

        if (t>0):
            #time.sleep(t)
        
            _.counter +=1
            for b in range(0, len(_.bodies)):
                x, y, z = _.bodies[b].getPosition()
                R = _.bodies[b].getRotation()
           
                rotMatrix =  Mat4(R[0], R[3], R[6], 0.,
                                  R[1], R[4], R[7], 0.,
                                  R[2], R[5], R[8], 0.,
                                     x,    y,    z, 1.0)
           
                _.solids[b].setMat(rotMatrix)
                #_.solids[b].setScale(_.scale)


            _.space.collide((_.world, _.contactgroup), _.near_callback)
            _.world.quickStep(t)
            _.contactgroup.empty()
        
        _.prevtime = task.time

        return Task.cont 

def main():
    fieldsize = FIELDSIZE
    fieldhalf = fieldsize / 2
    spacing = BUILDING_SPACING
    
    roboWorld = WorldMgr(FIELDSIZE)

    #foofont = loader.loadFont(DATAPATH+'bitwise.ttf')

    # Create Ambient Light
    ambientLight = AmbientLight( 'ambientLight' )
    ambientLight.setColor( Vec4( 0.5, 0.5, 0.5, 1 ) )
    ambientLightNP = render.attachNewNode( ambientLight.upcastToPandaNode() )
    render.setLight(ambientLightNP)

    # EINS .... HIER KOMMT DIE SONNE
    directionalLight = DirectionalLight( "directionalLight" )
    # ZWEI .... HIER KOMMT DIE SONNE
    directionalLight.setColor( Vec4( 1.0, 1.0, 1.0, 1 ) )
    # DREI .... SIE IST DER SCHOENSTE STERN VON ALLEN
    sonne = loader.loadModel(DATAPATH+'/sphere')
    sonne.reparentTo(render)
    # VIER .... HIER KOMMT DIE SONNE
    directionalLightNP = sonne.attachNewNode( directionalLight.upcastToPandaNode() )
    directionalLightNP.setHpr(315, -30, 0)
    render.setLight(directionalLightNP)
    sonne.setPos(100,100,30)


    redRobot = Robot(roboWorld)
    redRobot.robo.setPos(0,0,2.5)
    
    # dirty, dirty hacks that would cause brain damage if attempted with C++
    redRobot.worldmgr = roboWorld    
    roboWorld.roboREF = redRobot
    
    # right now roboBody is designed only for ground contact...
    # needs to be redone if robo is going to get shot at
    roboBody = roboWorld.addPandaBody(redRobot.robo, 'robot',
        CollisionSphere(0,0,-2.5,1.0))
        #CollisionSphere(0,0,-0.6,2.0)) #Vec3(3,1,4))
    playaCam = RoboCamera()
    playa = Controls(redRobot, playaCam)
    redRobot.camREF = playaCam
    
##    tank = procmodels.loadModel(procmodels.tank)
##    tank.reparentTo(render)
##    tank.setPos(-3,-5,0)
##
##    h = procmodels.loadModel(procmodels.soldier)
##    h.reparentTo(render)
##    h.setPos(-2,-5,0)

    for x in range(-fieldhalf/spacing, fieldhalf/spacing):
        for y in range(-fieldhalf/spacing, fieldhalf/spacing):
            if random.uniform(0,1) <= BUILDING_DENSITY:
                foo = Building(roboWorld)
                foo.node.setPos((x * spacing),
                                (y * spacing),
                                0)

    for i in range(0, HUMANS):
        roboWorld.addHuman()
        
    for i in range(0, CARS):
        roboWorld.addCar()

    UseToonShader = False
    HardShader = True

##    if MUSICVOLUME > 0.0:
##        audio.playMusic('game', MUSICVOLUME)

    if (base.win.getGsg().getSupportsBasicShaders() and UseToonShader is True):
        if HardShader is False:
            tempnode = NodePath(PandaNode("temp node"))
            tempnode.setAttrib(LightRampAttrib.makeSingleThreshold(0.5, 0.4))
            tempnode.setShaderAuto()
            base.cam.node().setInitialState(tempnode.getState())
        
        filters = CommonFilters(base.win, base.cam)
        filters.setCartoonInk(separation=1.0)
        run()
    else:
        run()
